package io.legado.app.ui.book.read.page.provider

import android.graphics.Typeface
import android.net.Uri
import android.os.Build
import android.text.Layout
import android.text.StaticLayout
import android.text.TextPaint
import com.novel.read.App
import com.novel.read.constant.AppPattern
import com.novel.read.data.db.entity.Book
import com.novel.read.data.db.entity.BookChapter
import com.novel.read.data.read.TextChapter
import com.novel.read.data.read.TextChar
import com.novel.read.data.read.TextLine
import com.novel.read.data.read.TextPage
import com.novel.read.help.AppConfig
import com.novel.read.help.ReadBookConfig
import com.novel.read.utils.NetworkUtils
import com.novel.read.utils.RealPathUtil
import com.novel.read.utils.ext.*
import java.util.*


@Suppress("DEPRECATION")
object ChapterProvider {

    /**
     * 页面显示宽度
     */
    private var viewWidth = 0

    /***
     * 页面显示高度
     */
    private var viewHeight = 0

    /***
     * 左边距
     */
    var paddingLeft = 0

    /***
     * 上边距
     */
    var paddingTop = 0

    /**
     * 可视宽度
     */
    var visibleWidth = 0

    /***
     * 可视高度
     */
    var visibleHeight = 0

    /***
     * 可视的右边位置
     */
    var visibleRight = 0

    /***
     * 可视底部位置
     */
    var visibleBottom = 0

    /***
     * 行间距
     */
    private var lineSpacingExtra = 0

    /***
     * 段落间距
     */
    private var paragraphSpacing = 0

    /***
     * 标题顶部间距
     */
    private var titleTopSpacing = 0

    /***
     * 标题底部间距
     */
    private var titleBottomSpacing = 0

    /***
     * 字体
     */
    var typeface: Typeface = Typeface.SANS_SERIF

    /***
     * 标题的TextPaint
     */
    lateinit var titlePaint: TextPaint

    /***
     * 文本内容的TextPaint
     */
    lateinit var contentPaint: TextPaint

    init {
        upStyle()
    }

    /**
     * 获取拆分完的章节数据
     */
    fun getTextChapter(
        book: Book,
        bookChapter: BookChapter,
        contents: List<String>,
        chapterSize: Int,
        imageStyle: String?,
    ): TextChapter {
        val textPages = arrayListOf<TextPage>()
        val pageLines = arrayListOf<Int>()  //显示的每一页的行数
        val pageLengths = arrayListOf<Int>()
        val stringBuilder = StringBuilder()
        var durY = 0f
        textPages.add(TextPage()) //
        contents.forEachIndexed { index, text -> //遍历内容列表， 一个段落一个段落（图片）的遍历
            val matcher = AppPattern.imgPattern.matcher(text) //匹配图片链接
            if (matcher.find()) {
                var src = matcher.group(1)
//                if (!book.isEpub()) {
                    src = NetworkUtils.getAbsoluteURL("", src)
//                }
                src?.let {
                    durY = setTypeImage(book, bookChapter, src, durY, textPages, imageStyle)
                }
            } else {
                val isTitle = index == 0 //如果没有图片，index为0就是标题页
                if (!(isTitle && ReadBookConfig.titleMode == 2)) {
                    durY =
                        setTypeText(
                            text, durY, textPages, pageLines,
                            pageLengths, stringBuilder, isTitle
                        )
                }
            }
        }
        //所有的段落/图片都遍历完之后
        textPages.last().height = durY + 20.dp                  //最后一个TextPage的高度加上20dp
        textPages.last().text = stringBuilder.toString()        //最后一个TextPage，设置没有统计到的文字
        if (pageLines.size < textPages.size) {                  //每一页的行数统计列表如果小于TextPage列表，则添加行数统计
            pageLines.add(textPages.last().textLines.size)
        }
        if (pageLengths.size < textPages.size) {                //每一页的字数统计列表如果小于TextPage列表，则添加字数统计
            pageLengths.add(textPages.last().text.length)
        }
        textPages.forEachIndexed { index, item ->               //遍历每一个TextPage
            item.index = index                                  //设置TextPage在所在章节中的索引位置
            item.pageSize = textPages.size                      //设置TextPage所在章节的页数
            item.chapterIndex = bookChapter.chapterIndex        //设置TextPage的章节索引
            item.chapterSize = chapterSize                      //设置TextPage的章节总数量
            item.title = bookChapter.chapterName                //设置TextPage的章节名称
            item.upLinesPosition()
        }

        return TextChapter(                                     //返回一个章节拆分到每一页之后的数据对象
            bookChapter.chapterIndex,                           //章节索引
            bookChapter.chapterName,                            //章节名
            bookChapter.chapterId.toInt(),                      //章节id
            textPages,                                          //分拆页的列表
            pageLines,                                          //每一页行数的集合
            pageLengths,                                        //每一页字数的集合
            chapterSize                                         //章节大小
        )
    }

    /****
     * 根据图片设置TextLine/TextChar属性，将结果保存到textPages中，并返回durY，用于计算下一行内容
     */
    private fun setTypeImage(
        book: Book,
        chapter: BookChapter,
        src: String,
        y: Float,
        textPages: ArrayList<TextPage>,
        imageStyle: String?,
    ): Float {
        var durY = y
        //从缓存中得到图片的bitmap
        ImageProvider.getImage(book, chapter.chapterId.toInt(), src)?.let {
            if (durY > visibleHeight) {  //当前可显示的高度位置超过了可视高度
                textPages.last().height = durY
                textPages.add(TextPage())
                durY = 0f
            }
            var height = it.height //获得bitmap图片的宽高
            var width = it.width
            when (imageStyle?.toUpperCase(Locale.ROOT)) { //撑满可视宽度显示
                "FULL" -> {
                    width = visibleWidth
                    height = it.height * visibleWidth / it.width
                }
                else -> {   //自适应显示
                    if (it.width > visibleWidth) {
                        height = it.height * visibleWidth / it.width
                        width = visibleWidth
                    }
                    if (height > visibleHeight) {
                        width = width * visibleHeight / height
                        height = visibleHeight
                    }
                    if (durY + height > visibleHeight) {
                        textPages.last().height = durY
                        textPages.add(TextPage())
                        durY = 0f
                    }
                }
            }
            val textLine = TextLine(isImage = true)
            textLine.lineTop = durY
            durY += height
            textLine.lineBottom = durY
            //图片显示水平方向的开始和结束位置
            val (start, end) = if (visibleWidth > width) {  //图片宽度小于可视宽度
                val adjustWidth = (visibleWidth - width) / 2f //左偏移的量
                Pair(
                    paddingLeft.toFloat() + adjustWidth,
                    paddingLeft.toFloat() + adjustWidth + width
                )
            } else {
                Pair(paddingLeft.toFloat(), (paddingLeft + width).toFloat())
            }
            textLine.textChars.add(
                TextChar(
                    charData = src, //图片的链接url
                    start = start,  //图片水平开始位置
                    end = end,      //图片水平结束位置
                    isImage = true
                )
            )
            textPages.last().textLines.add(textLine)
        }
        return durY + paragraphSpacing / 10f
    }

    /**
     * 排版文字
     */
    private fun setTypeText(
        text: String,
        y: Float,
        textPages: ArrayList<TextPage>,
        pageLines: ArrayList<Int>,
        pageLengths: ArrayList<Int>,
        stringBuilder: StringBuilder,
        isTitle: Boolean,
    ): Float {
        var durY = if (isTitle) y + titleTopSpacing else y
        val textPaint = if (isTitle) titlePaint else contentPaint
        val layout = StaticLayout(
            text, textPaint, visibleWidth, Layout.Alignment.ALIGN_NORMAL, 0f, 0f, true
        )
        for (lineIndex in 0 until layout.lineCount) { //排版，遍历按行显示
            val textLine = TextLine(isTitle = isTitle) //是否是标题行
            //截取一行显示的文字内容
            val words = text.substring(layout.getLineStart(lineIndex), layout.getLineEnd(lineIndex))
            //得到期望的行宽
            val desiredWidth = layout.getLineWidth(lineIndex)
            var isLastLine = false //是否是最后一行

            if (lineIndex == 0 && layout.lineCount > 1 && !isTitle) { //不是标题行，并且是这个段落的第一行
                //第一行
                textLine.text = words
                addCharsToLineFirst(
                    textLine,
                    words.toStringArray(),
                    textPaint,
                    desiredWidth
                )
            } else if (lineIndex == layout.lineCount - 1) {
                //最后一行
                textLine.text = "$words\n"
                isLastLine = true
                val x = if (isTitle && ReadBookConfig.titleMode == 1)
                    (visibleWidth - layout.getLineWidth(lineIndex)) / 2
                else 0f
                addCharsToLineLast(
                    textLine,
                    words.toStringArray(),
                    textPaint,
                    x
                )
            } else {
                //中间行
                textLine.text = words
                addCharsToLineMiddle(
                    textLine,
                    words.toStringArray(),
                    textPaint,
                    desiredWidth,
                    0f
                )
            }
            // 当前行的测量 超过了一屏
            if (durY + textPaint.textHeight > visibleHeight) {
                //当前页面结束,设置各种值
                textPages.last().text = stringBuilder.toString()
                pageLines.add(textPages.last().textLines.size)  //设置一个TextPage中有多少行，加入到pageLines中
                pageLengths.add(textPages.last().text.length)   //设置一个Textpage中显示的字符的个数
                textPages.last().height = durY                  //设置一个TextPage显示的高度

                textPages.add(TextPage())                       //新建页面
                stringBuilder.clear()                           //清空字数的缓存
                durY = 0f                                       //新的一页的durY置为0
            }
            stringBuilder.append(words)                         //添加一个页面显示字符串，用于统计字数

            if (isLastLine) stringBuilder.append("\n")          //如果是一个段落的最后一行，增加一个换行符
            textPages.last().textLines.add(textLine)            //将上面测量之后的TextLine加入到TextPage的集合中
            textLine.upTopBottom(durY, textPaint)               //设置一个TextLine的上，下，基线位置
            durY += textPaint.textHeight * lineSpacingExtra / 10f   //将行间距加入到durY值中
            textPages.last().height = durY                      //加完之后，计算得到一个TextPage的显示高度
        }
        //正文一个段落的每一行都遍历完之后
        if (isTitle) durY += titleBottomSpacing                 //如果是标题行，加上标题底部间距
        durY += textPaint.textHeight * paragraphSpacing / 10f   //将段落间距加入到durY值中
        return durY
    }

    /**
     * 有缩进,两端对齐
     */
    private fun addCharsToLineFirst(
        textLine: TextLine,
        words: Array<String>,
        textPaint: TextPaint,
        desiredWidth: Float,
    ) {
        var x = 0f
        if (!ReadBookConfig.textFullJustify) { //如果不需要两端对齐，则默认从左向右显示，计算每个字母的显示位置
            addCharsToLineLast(
                textLine,
                words,
                textPaint,
                x
            )
            return
        }
        val bodyIndent = ReadBookConfig.paragraphIndent //段落首行缩进的字符串（tab换行符）
        val icw = StaticLayout.getDesiredWidth(bodyIndent, textPaint) / bodyIndent.length//缩进的字符串的单个字符宽度
        bodyIndent.toStringArray().forEach {
            val x1 = x + icw
            textLine.addTextChar( //将首行缩进的字符加入到textLine的TextChar数组中
                charData = it,
                start = paddingLeft + x,
                end = paddingLeft + x1
            )
            x = x1
        }
        val words1 = words.copyOfRange(bodyIndent.length, words.size) //计算剩余的字符
        addCharsToLineMiddle( //将剩余的字符塞进一行显示
            textLine,
            words1,
            textPaint,
            desiredWidth,
            x
        )
    }

    /**
     * 无缩进,两端对齐
     */
    private fun addCharsToLineMiddle(
        textLine: TextLine,
        words: Array<String>,
        textPaint: TextPaint,
        desiredWidth: Float,
        startX: Float,
    ) {
        if (!ReadBookConfig.textFullJustify) { //不是两端对齐，
            addCharsToLineLast(
                textLine,
                words,
                textPaint,
                startX
            )
            return
        }
        val gapCount: Int = words.lastIndex //根据索引得到中间需要加入的间隔数量
        val d = (visibleWidth - desiredWidth) / gapCount  //得到每一个间隔的宽度
        var x = startX
        words.forEachIndexed { index, s -> //遍历每一个显示的字符
            val cw: Float = StaticLayout.getDesiredWidth(s, textPaint)  //单个字符显示的宽度
            val x1 = if (index != words.lastIndex) (x + cw + d) else (x + cw)
            textLine.addTextChar(
                charData = s,
                start = paddingLeft + x,
                end = paddingLeft + x1
            )
            x = x1
        }
        exceed(
            textLine,
            words
        )
    }

    /**
     * 从左向右， 自然排列
     */
    private fun addCharsToLineLast(
        textLine: TextLine,
        words: Array<String>,
        textPaint: TextPaint,
        startX: Float,
    ) {
        var x = startX
        words.forEach {
            val cw = StaticLayout.getDesiredWidth(it, textPaint)
            val x1 = x + cw
            textLine.addTextChar(
                charData = it,
                start = paddingLeft + x,
                end = paddingLeft + x1
            )
            x = x1
        }
        exceed(
            textLine,
            words
        )
    }

    /**
     * 超出边界处理
     */
    private fun exceed(textLine: TextLine, words: Array<String>) {
        val endX = textLine.textChars.last().end //最后一个字符的左侧
        if (endX > visibleRight) {  //超出可视的右侧
            val cc = (endX - visibleRight) / words.size //超出的偏移量平均分到每个字母上
            for (i in 0..words.lastIndex) {
                textLine.getTextCharReverseAt(i).let {  //重新遍历每个字母，计算其水平方向上的开始和结束位置
                    val py = cc * (words.size - i)
                    it.start = it.start - py
                    it.end = it.end - py
                }
            }
        }
    }

    /**
     * 更新样式
     */
    fun upStyle() {
        //更新字体
        typeface = try {
            val fontPath = ReadBookConfig.textFont  //字体路径
            when {
                //android26以上版本, 根据file descriptor得到字体类
                fontPath.isContentPath() && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O -> {
                    val fd = App.INSTANCE.contentResolver
                        .openFileDescriptor(Uri.parse(fontPath), "r")!!
                        .fileDescriptor
                    Typeface.Builder(fd).build()
                }
                //android26以下版本，直接解析得到字体文件路径，从而得到字体类
                fontPath.isContentPath() -> {
                    Typeface.createFromFile(RealPathUtil.getPath(App.INSTANCE, Uri.parse(fontPath)))
                }
                //如果就是字体文件路径，直接得到字体类
                fontPath.isNotEmpty() -> Typeface.createFromFile(fontPath)
                //否则采用系统字体
                else -> when (AppConfig.systemTypefaces) {
                    1 -> Typeface.SERIF
                    2 -> Typeface.MONOSPACE
                    else -> Typeface.SANS_SERIF
                }
            }
        } catch (e: Exception) {
            ReadBookConfig.textFont = ""
            ReadBookConfig.save()
            Typeface.SANS_SERIF
        }
        // 字体统一处理
        val bold = Typeface.create(typeface, Typeface.BOLD) //加粗
        val normal = Typeface.create(typeface, Typeface.NORMAL) //正常
        //根据用户配置是否加粗，得到标题字体和内容字体
        val (titleFont, textFont) = when (ReadBookConfig.textBold) {
            1 -> {
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P)
                    Pair(Typeface.create(typeface, 900, false), bold)
                else
                    Pair(bold, bold)
            }
            2 -> {
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P)
                    Pair(normal, Typeface.create(typeface, 300, false))
                else
                    Pair(normal, normal)
            }
            else -> Pair(bold, normal)
        }

        //标题的Paint
        titlePaint = TextPaint()
        titlePaint.color = ReadBookConfig.textColor                                         //设置标题文字颜色
        titlePaint.letterSpacing = ReadBookConfig.letterSpacing                             //设置标题字母间距
        titlePaint.typeface = titleFont                                                     //设置标题字体
        titlePaint.textSize = with(ReadBookConfig) { textSize + titleSize }.sp.toFloat()    //设置标题字体大小
        titlePaint.isAntiAlias = true                                                       //设置抗锯齿
        //正文的Paint
        contentPaint = TextPaint()
        contentPaint.color = ReadBookConfig.textColor                                       //设置正文文字颜色
        contentPaint.letterSpacing = ReadBookConfig.letterSpacing                           //设置正文文字间距
        contentPaint.typeface = textFont                                                    //设置正文字体
        contentPaint.textSize = ReadBookConfig.textSize.sp.toFloat()                        //设置字体大小
        contentPaint.isAntiAlias = true                                                     //设置抗锯齿
        //间距
        lineSpacingExtra = ReadBookConfig.lineSpacingExtra                                  //行间距
        paragraphSpacing = ReadBookConfig.paragraphSpacing                                  //段落缩进
        titleTopSpacing = ReadBookConfig.titleTopSpacing.dp                                 //标题顶部间距
        titleBottomSpacing = ReadBookConfig.titleBottomSpacing.dp                           //标题底部间距
        //更新屏幕参数
        upVisibleSize()
    }

    /**
     * 更新View尺寸
     */
    fun upViewSize(width: Int, height: Int) {
        if (width > 0 && height > 0) {
            viewWidth = width
            viewHeight = height
            upVisibleSize()
        }
    }

    /**
     * 更新绘制尺寸
     */
    private fun upVisibleSize() {
        if (viewWidth > 0 && viewHeight > 0) {
            paddingLeft = ReadBookConfig.paddingLeft.dp                                     //页面左边距
            paddingTop = ReadBookConfig.paddingTop.dp                                       //页面顶部间距
            visibleWidth = viewWidth - paddingLeft - ReadBookConfig.paddingRight.dp         //可视宽度
            visibleHeight = viewHeight - paddingTop - ReadBookConfig.paddingBottom.dp       //可视高度
            visibleRight = paddingLeft + visibleWidth                                       //可视右边
            visibleBottom = paddingTop + visibleHeight                                      //可视底部
        }
    }

    val TextPaint.textHeight: Float
        get() = fontMetrics.descent - fontMetrics.ascent + fontMetrics.leading
}
